bin/checkout: add --selinux-policy switch
authorJonathan Lebon <jlebon@redhat.com>
Thu, 1 Feb 2018 22:10:47 +0000 (22:10 +0000)
committerAtomic Bot <atomic-devel@projectatomic.io>
Fri, 2 Feb 2018 22:36:49 +0000 (22:36 +0000)
This was already supported by the underlying API. Expose it so that we
can test it.

Closes: #1442
Approved by: cgwalters

src/ostree/ot-builtin-checkout.c
tests/installed/itest-label-selinux.sh

index 533cfd7d4841eb6aca96abc9a425204364d736d0..db5507e769b8bbb6189de397b33ae17d73167e14 100644 (file)
@@ -46,6 +46,8 @@ static gboolean opt_disable_fsync;
 static gboolean opt_require_hardlinks;
 static gboolean opt_force_copy;
 static gboolean opt_bareuseronly_dirs;
+static char *opt_selinux_policy;
+static char *opt_selinux_prefix;
 
 static gboolean
 parse_fsync_cb (const char  *option_name,
@@ -57,7 +59,7 @@ parse_fsync_cb (const char  *option_name,
 
   if (!ot_parse_boolean (value, &val, error))
     return FALSE;
-    
+
   opt_disable_fsync = !val;
 
   return TRUE;
@@ -83,6 +85,8 @@ static GOptionEntry options[] = {
   { "require-hardlinks", 'H', 0, G_OPTION_ARG_NONE, &opt_require_hardlinks, "Do not fall back to full copies if hardlinking fails", NULL },
   { "force-copy", 'C', 0, G_OPTION_ARG_NONE, &opt_force_copy, "Never hardlink (but may reflink if available)", NULL },
   { "bareuseronly-dirs", 'M', 0, G_OPTION_ARG_NONE, &opt_bareuseronly_dirs, "Suppress mode bits outside of 0775 for directories (suid, world writable, etc.)", NULL },
+  { "selinux-policy", 0, 0, G_OPTION_ARG_FILENAME, &opt_selinux_policy, "Set SELinux labels based on policy in root filesystem PATH (may be /); implies --force-copy", "PATH" },
+  { "selinux-prefix", 0, 0, G_OPTION_ARG_STRING, &opt_selinux_prefix, "When setting SELinux labels, prefix all paths by PREFIX", "PREFIX" },
   { NULL }
 };
 
@@ -102,10 +106,15 @@ process_one_checkout (OstreeRepo           *repo,
    * convenient infrastructure for testing C APIs with data.
    */
   if (opt_disable_cache || opt_whiteouts || opt_require_hardlinks ||
-      opt_union_add || opt_force_copy || opt_bareuseronly_dirs || opt_union_identical)
+      opt_union_add || opt_force_copy || opt_bareuseronly_dirs || opt_union_identical ||
+      opt_selinux_policy || opt_selinux_prefix)
     {
       OstreeRepoCheckoutAtOptions options = { 0, };
 
+      /* do this early so option checking also catches force copy conflicts */
+      if (opt_selinux_policy)
+        opt_force_copy = TRUE;
+
       if (opt_user_mode)
         options.mode = OSTREE_REPO_CHECKOUT_MODE_USER;
       /* Can't union these */
@@ -132,6 +141,11 @@ process_one_checkout (OstreeRepo           *repo,
           glnx_throw (error, "Cannot specify both --require-hardlinks and --force-copy");
           goto out;
         }
+      if (opt_selinux_prefix && !opt_selinux_policy)
+        {
+          glnx_throw (error, "Cannot specify --selinux-prefix without --selinux-policy");
+          goto out;
+        }
       else if (opt_union)
         options.overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES;
       else if (opt_union_add)
@@ -150,6 +164,23 @@ process_one_checkout (OstreeRepo           *repo,
         options.process_whiteouts = TRUE;
       if (subpath)
         options.subpath = subpath;
+
+      g_autoptr(OstreeSePolicy) policy = NULL;
+      if (opt_selinux_policy)
+        {
+          glnx_autofd int rootfs_dfd = -1;
+          if (!glnx_opendirat (AT_FDCWD, opt_selinux_policy, TRUE, &rootfs_dfd, error))
+            {
+              g_prefix_error (error, "selinux-policy: ");
+              goto out;
+            }
+          policy = ostree_sepolicy_new_at (rootfs_dfd, cancellable, error);
+          if (!policy)
+            goto out;
+          options.sepolicy = policy;
+          options.sepolicy_prefix = opt_selinux_prefix;
+        }
+
       options.no_copy_fallback = opt_require_hardlinks;
       options.force_copy = opt_force_copy;
       options.bareuseronly_dirs = opt_bareuseronly_dirs;
@@ -201,7 +232,7 @@ process_one_checkout (OstreeRepo           *repo,
                                       cancellable, error))
         goto out;
     }
-                      
+
   ret = TRUE;
  out:
   return ret;
@@ -234,7 +265,7 @@ process_many_checkouts (OstreeRepo         *repo,
       if (!instream)
         goto out;
     }
-    
+
   datastream = g_data_input_stream_new (instream);
 
   while ((revision = g_data_input_stream_read_upto (datastream, "", 1, &len,
index 1218a8bc8a70d46c03376c3e1bf486bf8c3542e2..d6244b3a6dd569d3935fe9f41e8f5a2bb9ff8ffe 100755 (executable)
@@ -20,11 +20,56 @@ oldcon=$(getfattr --only-values -m security.selinux ${testbin})
 chcon --reference co/usr/bin/bash ${testbin}
 newcon=$(getfattr --only-values -m security.selinux ${testbin})
 assert_not_streq "${oldcon}" "${newcon}"
-ostree --repo=/ostree/repo commit -b testbranch --link-checkout-speedup \
+ostree commit -b testbranch --link-checkout-speedup \
        --selinux-policy co --tree=dir=co
-ostree --repo=/ostree/repo ls -X testbranch /usr/bin/foo-a-generic-binary > ls.txt
+ostree ls -X testbranch /usr/bin/foo-a-generic-binary > ls.txt
 assert_file_has_content ls.txt ${oldcon}
-ostree --repo=/ostree/repo fsck
+ostree fsck
 
-ostree --repo=/ostree/repo refs --delete testbranch
+ostree refs --delete testbranch
 rm co -rf
+echo "ok commit with sepolicy"
+
+# Now let's check that selinux policy labels can be applied on checkout
+
+rm rootfs -rf
+if ostree checkout -H \
+    --selinux-policy / ${host_refspec} co; then
+  assert_not_reached "checked out with -H and --selinux-policy"
+fi
+# recommit just two binaries into a new branch with selinux labels stripped
+mkdir -p rootfs/usr/bin
+oldcon=$(getfattr --only-values -m security.selinux /usr/bin/bash)
+cp /usr/bin/{true,bash} rootfs/usr/bin
+newcon=$(getfattr --only-values -m security.selinux rootfs/usr/bin/bash)
+assert_not_streq "${oldcon}" "${newcon}"
+echo "ok checkout with sepolicy setup"
+
+ostree commit -b testbranch rootfs
+ostree checkout testbranch --selinux-policy / co
+newcon=$(getfattr --only-values -m security.selinux co/usr/bin/bash)
+assert_streq "${oldcon}" "${newcon}"
+rm co -rf
+echo "ok checkout with sepolicy"
+ostree checkout testbranch --selinux-policy / --subpath /usr/bin co
+newcon=$(getfattr --only-values -m security.selinux co/bash)
+assert_streq "${oldcon}" "${newcon}"
+rm co -rf
+echo "ok checkout with sepolicy and subpath"
+
+# now commit tree with mismatched leading dirs
+mkdir -p rootfs/subdir
+mv rootfs/{usr,subdir}
+ostree commit -b testbranch rootfs
+ostree checkout testbranch --selinux-policy / co
+newcon=$(getfattr --only-values -m security.selinux co/subdir/usr/bin/bash)
+assert_not_streq "${oldcon}" "${newcon}"
+rm co -rf
+ostree checkout testbranch --selinux-policy / \
+  --subpath subdir --selinux-prefix / co
+newcon=$(getfattr --only-values -m security.selinux co/usr/bin/bash)
+assert_streq "${oldcon}" "${newcon}"
+
+ostree refs --delete testbranch
+rm co -rf
+echo "ok checkout with sepolicy and selinux-prefix"